# -*- coding: utf-8 -*-
"""
Created on Tue Aug 13 14:30:46 2013

@author: John Edwardson
"""

import wx
import wx.wizard as wizmod
import os.path, sys
import subprocess as sp
import ConfigParser
from dialogs import UnitPanel, WinSettingsPanel, FilePanel, DisPanel
padding = 5


def show_wiz():
    '''Creates and runs the plot setup wizard.  Returns the filename where configuration
    settings are stored'''
    #create wizard and pages    
    setup_wiz = wizard('Plot Setup Wizard', img_filename='wiz.png')
    pages = [wizard_mol_page(setup_wiz, 'Molecule'), 
             wizard_win_page(setup_wiz, 'Window'), 
             wizard_file_page(setup_wiz, 'Data Files'), 
             wizard_styles_page(setup_wiz, 'Colors and Styles'), 
             wizard_save_page(setup_wiz, 'Save Plot Settings'),
             wizard_preview_page(setup_wiz, 'Preview')]    
    for page in pages:
        setup_wiz.add_page(page)
        
    wiz_sizer = setup_wiz.GetPageAreaSizer()
    wiz_sizer.Add(pages[0])
          
    setup_wiz.run() # Show the main window
    output = setup_wiz.filename
   
    # Cleanup
    setup_wiz.Destroy()
    return output
       


class wizard_page(wizmod.PyWizardPage):
    ''' An extended panel obj with a few methods to keep track of its siblings.  
        This should be modified and added to the wizard.  Season to taste.'''
    def __init__(self, parent, title):
        wx.wizard.PyWizardPage.__init__(self, parent)
        self.next = self.prev = None
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        title = wx.StaticText(self, -1, title)
        title.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD))
        self.sizer.AddWindow(title, 0, wx.ALIGN_LEFT|wx.ALL, padding)
        self.sizer.AddWindow(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, padding)
        

    def add_stuff(self, stuff):
        '''Add aditional widgets to the bottom of the page'''
        self.sizer.Add(stuff, 0, wx.EXPAND|wx.ALL, padding)

    def SetNext(self, next):
        '''Set the next page'''
        self.next = next

    def SetPrev(self, prev):
        '''Set the previous page'''
        self.prev = prev

    def GetNext(self):
        '''Return the next page'''
        return self.next

    def GetPrev(self):
        '''Return the previous page'''
        return self.prev
        
class wizard_mol_page(wizard_page):
    def __init__(self, parent, title):
        '''Molecule Settings and Dissociation Limits Page'''        
        wizard_page.__init__(self, parent, title)
        self.dispanel = DisPanel(self)
        self.sizer.Add(self.dispanel, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.Layout()
           
    def write_output(self, parser):
        '''Get output and write to parser'''        
        try: #ConfigParser doesnt allow repeated section names        
            parser.add_section("Molecule")
        except: 
            pass
        
        msg = self.dispanel.sanity_check()
        if msg != '':
            return msg        
        output = self.dispanel.getOutput()
        for key in output:
            parser.set("Molecule", key, output[key])        
        return ''

class wizard_win_page(wizard_page):
    def __init__(self, parent, title):
        '''Window Settings Page'''
        wizard_page.__init__(self, parent, title)
        
        defaults = dict(absmin = -5000., absmax = 30000., winrange = 15000.,
                        xmin = 0., xmax = 20., xtics = 2., ytics = 2000.,
                        ymin = 0., ymax = 10000.)
      
        units = {'Radius': 'Ang', 'Energy': 'wavenumbers', 'Scale': 'Molecular'}
        
        self.settings_panel = WinSettingsPanel(self, defaults)
        
        self.sizer.Add(self.settings_panel, 2, wx.ALL | wx.EXPAND , 5)
        self.unitPanel = UnitPanel(self, units)
        self.sizer.Add(self.unitPanel, 1, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5) 
        self.settings_panel.boxdict['ymax'].Disable()
        self.settings_panel.boxdict['ymin'].Disable()        
        
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.Layout()
       
                             
    def write_output(self, parser):
        try:        
            parser.add_section("Window")
        except: 
            pass
        msg = self.settings_panel.checkInput()
        if msg != '':
            return msg
        
        props = self.settings_panel.getProps()
        props.pop('ymin')
        props.pop('ymax')
     
        for k in props:
            parser.set("Window", k, props[k])
            
        try:
            parser.add_section("Units")
        except:
            pass
        units = self.unitPanel.getUnits()
        for k in units:
            parser.set("Units", k, units[k])
        return ''


class wizard_file_page(wizard_page):
    
    def __init__(self, parent, title):
        wizard_page.__init__(self, parent, title)
        
        self.fstring = "{}.txt"
        self.units = {'Radius': 'AU', 'Energy': 'wavenumbers', 'Scale': 'Molecular'}
        self.filepanel = FilePanel(self, self.units, self.fstring )
        
        self.sizer.Add(self.filepanel, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        self.sizer.Layout()

        
    def sanity_check(self):
        '''Makes sure input makes sense.  If an error is detected, returns a string
        describing the error(returns on first error found)'''
                  
        return ''
        
    def write_output(self, parser):
        try:        
            parser.add_section("Files")
        except: 
            pass
        
        msg = self.sanity_check()
        if msg != '':
            return msg
            
        for fileinfo in self.filepanel.datafiles:
            line = ''
            for item in fileinfo[1:-1]:
                line += item + ", "
            line += str(fileinfo[-1])
            parser.set("Files", fileinfo[0], line)
   
        return ''

class wizard_styles_page(wizard_page):
    def __init__(self, parent, title):
        wizard_page.__init__(self, parent, title)

        self.sigma = wx.StaticText(self, -1, "Sigma: ", style=wx.ALIGN_CENTRE)
        self.sbox = wx.ComboBox(self, wx.ID_ANY, choices=["black", "green", "red", "cyan", "magenta", "yellow", "blue", "white", "more..."], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.delta = wx.StaticText(self, -1, "Delta: ", style=wx.ALIGN_CENTRE)
        self.dbox = wx.ComboBox(self, wx.ID_ANY, choices=["black", "green", "red", "cyan", "magenta", "yellow", "blue", "white", "more..."], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.pi = wx.StaticText(self, -1, "Pi: ", style=wx.ALIGN_CENTRE)
        self.pbox = wx.ComboBox(self, wx.ID_ANY, choices=["black", "green", "red", "cyan", "magenta", "yellow", "blue", "white", "more..."], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.phi = wx.StaticText(self, -1, "Phi: ", style=wx.ALIGN_CENTRE)
        self.fbox = wx.ComboBox(self, wx.ID_ANY, choices=["black", "green", "red", "cyan", "magenta", "yellow", "blue", "white", "more..."], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.color_sizer_staticbox = wx.StaticBox(self, -1, "Colors")
        self.singlet = wx.StaticText(self, -1, "Singlet: ", style=wx.ALIGN_CENTRE)
        self.singbox = wx.ComboBox(self, -1, choices=["-", "--", "-.", ":"], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.doublet = wx.StaticText(self, -1, "Doublet: ", style=wx.ALIGN_CENTRE)
        self.doubbox = wx.ComboBox(self, -1, choices=["-", "--", "-.", ":"], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.triplet = wx.StaticText(self, -1, "Triplet: ", style=wx.ALIGN_CENTRE)
        self.tripbox = wx.ComboBox(self, -1, choices=["-", "--", "-.", ":"], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN | wx.CB_READONLY)
        self.style_sizer_staticbox = wx.StaticBox(self, -1, "Styles")
        self.gerade = wx.StaticText(self, -1, "Gerade: ", style=wx.ALIGN_CENTRE)
        self.gbox = wx.ComboBox(self, -1, choices=["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN)
        self.ungerade = wx.StaticText(self, -1, "Ungerade: ", style=wx.ALIGN_CENTRE)
        self.ubox = wx.ComboBox(self, -1, choices=["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], style=wx.CB_DROPDOWN | wx.CB_DROPDOWN)
        self.width_sizer_staticbox = wx.StaticBox(self, -1, "Linewidth")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_COMBOBOX, self.OnBox, self.sbox)
        self.Bind(wx.EVT_COMBOBOX, self.OnBox, self.dbox)
        self.Bind(wx.EVT_COMBOBOX, self.OnBox, self.pbox)
        self.Bind(wx.EVT_COMBOBOX, self.OnBox, self.fbox)
        

    def __set_properties(self):
        #set defaults
        self.sbox.SetSelection(0)
        self.dbox.SetSelection(6)
        self.pbox.SetSelection(1)
        self.fbox.SetSelection(2)
        self.singbox.SetSelection(0)
        self.doubbox.SetSelection(2)
        self.tripbox.SetSelection(1)
        self.gbox.SetSelection(0)
        self.ubox.SetSelection(1)
  

    def __do_layout(self):
        self.width_sizer_staticbox.Lower()
        width_sizer = wx.StaticBoxSizer(self.width_sizer_staticbox, wx.HORIZONTAL)
        width_grid = wx.GridSizer(2, 2, 0, 0)
        self.style_sizer_staticbox.Lower()
        style_sizer = wx.StaticBoxSizer(self.style_sizer_staticbox, wx.HORIZONTAL)
        style_grid = wx.GridSizer(2, 4, 0, 0)
        self.color_sizer_staticbox.Lower()
        color_sizer = wx.StaticBoxSizer(self.color_sizer_staticbox, wx.HORIZONTAL)
        state_grid = wx.GridSizer(2, 4, 0, 0)
        state_grid.Add(self.sigma, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.sbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.delta, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.dbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.pi, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.pbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.phi, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        state_grid.Add(self.fbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        color_sizer.Add(state_grid, 1, wx.EXPAND, 0)
        
        style_grid.Add(self.singlet, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        style_grid.Add(self.singbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        style_grid.Add(self.doublet, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        style_grid.Add(self.doubbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        style_grid.Add(self.triplet, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        style_grid.Add(self.tripbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        style_sizer.Add(style_grid, 1, wx.EXPAND, 0)
        
        width_grid.Add(self.gerade, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        width_grid.Add(self.gbox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        width_grid.Add(self.ungerade, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
        width_grid.Add(self.ubox, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        width_sizer.Add(width_grid, 1, wx.EXPAND, 0)

        self.sizer.Add(color_sizer, 1, wx.ALL | wx.EXPAND, 2)
        self.sizer.Add(style_sizer, 1, wx.ALL | wx.EXPAND, 1)
        self.sizer.Add(width_sizer, 1, wx.ALL | wx.EXPAND, 0)
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.Layout()
        # end wxGlade

    def OnBox(self, event):  
        '''Box seletion event handler.  When more... is chosen from one of the 
        color dropdowns, creates a color choice dialog'''
        box = event.GetEventObject()
        mpos = box.FindString("more...")        
        
        if event.GetSelection() == mpos:
            dlg = wx.ColourDialog(self)
            dlg.GetColourData().SetChooseFull(True)
 
            if dlg.ShowModal() == wx.ID_OK:
                data = dlg.GetColourData()
                box.Insert(data.GetColour().GetAsString(flags = wx.C2S_NAME | wx.C2S_HTML_SYNTAX), mpos)
                box.SetSelection(mpos)
     
            dlg.Destroy()
       

        
    def sanity_check(self):
        '''Makes sure input makes sense.  If an error is detected, returns a string
        describing the error(returns on first error found)'''
        cdict = {"Sigma": self.sbox.GetValue(), "Delta": self.dbox.GetValue(), "Pi": self.pbox.GetValue(), "Phi": self.fbox.GetValue() }        
        for k in cdict:
            if cdict[k] == '':
                return 'Missing color for ' + k + ' states'
        
        lsdict = {"Ungerade" : self.ubox.GetValue(), "Gerade" : self.gbox.GetValue()}
        for k in lsdict:
            if lsdict[k] == '':
                return 'Missing linewidth for ' + k + ' states'
            try:
                f = float(lsdict[k])
            except ValueError:
                return 'Invalid linewidth for ' + k + ' states'
        
        return ''
        
    def write_output(self, parser):
        try:        
            parser.add_section("Styles")
        except: 
            pass
        
        msg = self.sanity_check()
        if msg != '':
            return msg
       
        parser.set("Styles", "S", self.sbox.GetValue())
        parser.set("Styles", "P", self.pbox.GetValue())
        parser.set("Styles", "D", self.dbox.GetValue())
        parser.set("Styles", "F", self.fbox.GetValue())
        parser.set("Styles", "Singlets", self.singbox.GetValue())
        parser.set("Styles", "Doublets", self.doubbox.GetValue())
        parser.set("Styles", "Triplets", self.tripbox.GetValue())
        parser.set("Styles", "Gerade", self.gbox.GetValue())
        parser.set("Styles", "Ungerade", self.ubox.GetValue())

        return ''

class wizard_save_page(wizard_page):
    def __init__(self, parent, title):
        wizard_page.__init__(self, parent, title)
        self.parent = parent
        self.ftext = wx.StaticText(self, -1, "Choose File: ")
        self.filebox = wx.TextCtrl(self, -1, "")
        self.browse_button = wx.Button(self, -1, "Browse")
        self.__set_properties()
        self.__do_layout()
        self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.browse_button)
   
    def __set_properties(self):
        return

    def __do_layout(self):
        hsizer = wx.BoxSizer(wx.HORIZONTAL)
        hsizer.Add(self.ftext, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        hsizer.Add(self.filebox, 4, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)        
        hsizer.Add(self.browse_button, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)        
        self.sizer.Add(hsizer, 1, wx.ALL | wx.EXPAND, 2)
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.Layout()
        
    def OnBrowse(self, event):        
        wildcard = "Text File (*.pec)|*.pec|" \
                   "All Files (*.*)|*.*"
        
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultDir=os.getcwd(), 
            defaultFile="",
            wildcard=wildcard,
            style=wx.SAVE 
            )

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.filebox.SetValue(path)
        dlg.Destroy()
    
    def write_output(self, parser):
        '''Check that a file has been chosen and update the filename in the wizard object, then
        write the config file'''
        
        if self.GetPath() == '':
            return "No File Chosen"
        else:
            filename = self.GetPath()
            self.parent.filename = filename
            with open(filename, 'wb') as configfile:
                parser.write(configfile)
            return ''
    
    def GetPath(self):
        return self.filebox.GetValue()

class wizard_preview_page(wizard_page):
    def __init__(self, parent, title):
        
        wizard_page.__init__(self, parent, title)
        self.parent = parent
        self.open = wx.Button(self, wx.ID_ANY, "Open config file")
        self.ptext = wx.StaticText(self, -1, "Editor: ")        
        self.prog = wx.TextCtrl(self, wx.ID_ANY, "notepad.exe")
        self.Bind(wx.EVT_BUTTON, self.OnOpen, self.open)
        self.__set_properties()
        self.__do_layout()
        

    def __set_properties(self):
        return

    def __do_layout(self):
        self.sizer.Add(self.open, 0, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5)
        hsizer = wx.BoxSizer(wx.HORIZONTAL)
        hsizer.Add(self.ptext, 0, wx.ALL | wx.ALIGN_RIGHT, 5)        
        hsizer.Add(self.prog, 2, wx.ALL, 5)
        self.sizer.Add(hsizer, 1, wx.EXPAND | wx.ALL, 5)
        
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.Layout()
    def OnOpen(self, event):
        fileName = self.parent.filename
        programName = self.prog.GetValue()
        
        try:
            sp.Popen([programName, fileName])
        except OSError:
            wx.MessageBox("Error opening file: Either the editor or the filename cannot be found")
            

        

 
class wizard(wx.wizard.Wizard):
    '''Add pages to this wizard object to make it useful.'''
    def __init__(self, title, img_filename=""):
        # img could be replaced by a py string of bytes
        if img_filename and os.path.exists(img_filename):
                img = wx.Bitmap(img_filename)
        else:   img = wx.NullBitmap
        wx.wizard.Wizard.__init__(self, None, -1, title, img)
        self.pages = []
        self.filename = ''        
        self.parser = ConfigParser.ConfigParser()
        self.parser.optionxform = str
        
        # Lets catch the events
        
        self.Bind(wizmod.EVT_WIZARD_PAGE_CHANGING, self.on_page_changing)
        self.Bind(wizmod.EVT_WIZARD_CANCEL, self.on_cancel)
        self.Bind(wizmod.EVT_WIZARD_FINISHED, self.on_finished)

    def add_page(self, page):
        '''Add a wizard page to the list.'''
        if self.pages:
            previous_page = self.pages[-1]
            page.SetPrev(previous_page)
            previous_page.SetNext(page)
        self.pages.append(page)

    def run(self):
        self.RunWizard(self.pages[0])

    def on_page_changing(self, evt):
        '''Executed before the page changes, so we might veto it.'''
        if evt.GetDirection():  dir = "forward"
        else:                   dir = "backward"
        page = evt.GetPage()
        if page == self.pages[-1]:
            return
        if dir == "forward":
            msg = page.write_output(self.parser)
            if msg != '':
                wx.MessageBox(msg)
                evt.Veto()
        

    def on_cancel(self, evt):
        '''Cancel button has been pressed.  Clean up and exit without continuing.'''
        return 


    def on_finished(self, evt):
        '''Finish button has been pressed.  Clean up and exit.'''
        return 


if __name__ == '__main__':

    app = wx.PySimpleApp()  # Start the application
    size = []
    f = show_wiz()
    print f
    app.MainLoop()
